/*
 * Copyright (c) 2011 Imre Fazekas.
 *  All rights reserved.
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  Neither the name of the Brillien nor the names of its
 *  terms and concepts may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.streamline;

import com.vii.streamline.apikey.ApiKeyValidator;
import com.vii.streamline.json.JsonRpcError;
import com.vii.streamline.json.JsonRpcServices;
import com.vii.streamline.services.error.StreamLineException;
import com.vii.streamline.structures.collections.InnerMap;
import com.vii.streamline.web.HttpServices;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;

public abstract class CommServlet extends HttpServlet {

    protected int                   timeout;
    protected Logger                logger;

    protected boolean               ssoRequired;
    protected boolean               apiKeyRequired;
    protected ApiKeyValidator       apiKeyValidator;

    protected String                context;
    protected String                serviceName;

    {
        timeout         = -1;
        logger          = LoggerFactory.getLogger(this.getClass());

        context         = "services";
        serviceName     = this.getClass().getSimpleName();
    }

    protected boolean checkApiKeyRequired(){
        String apiKey =  getServletConfig().getInitParameter("apiKeyRequired");
        return apiKey != null && Boolean.parseBoolean(apiKey);
    }

    protected boolean checkSsoRequired(){
        String apiKey =  getServletConfig().getInitParameter("ssoRequired");
        return apiKey != null && Boolean.parseBoolean(apiKey);
    }

    protected ApiKeyValidator getApiKeyValidator() throws ServletException {
        if( apiKeyValidator == null ){
            String validator =  getServletConfig().getInitParameter("apiKeyValidator");
            if( validator == null )
                throw new ServletException("Missing apiKeyValidator provider class setting!");

            try{
                apiKeyValidator = (ApiKeyValidator)this.getClass().getClassLoader().loadClass( validator ).newInstance();

                Map<String, Object> parameters = new InnerMap<String, Object>();
                Enumeration<String> configEnum = this.getServletConfig().getInitParameterNames();
                while( configEnum.hasMoreElements() ){
                    String parameterName = configEnum.nextElement();
                    parameters.put( parameterName, this.getServletConfig().getInitParameter( parameterName ) );
                }
                apiKeyValidator.init( parameters );
            } catch (Exception e){ throw new ServletException("Process to create an ApiKeyValidator failed!", e); }
        }
        return apiKeyValidator;
    }

    public String getContext() {
        return context;
    }

    public void setContext(String context) {
        this.context = context;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    @Override
    public void init() throws ServletException {
        super.init();

        ssoRequired = checkSsoRequired();

        apiKeyRequired = checkApiKeyRequired();
        if( apiKeyRequired )
            apiKeyValidator = getApiKeyValidator();
    }

    @Override
    public void log( String s ){
        logger.debug( s );
    }

    public boolean isSsoRequired() {
        return ssoRequired;
    }

    public void setSsoRequired(boolean ssoRequired) {
        this.ssoRequired = ssoRequired;
    }

    public boolean isApiKeyRequired() {
        return apiKeyRequired;
    }

    public void setApiKeyRequired(boolean apiKeyRequired) {
        this.apiKeyRequired = apiKeyRequired;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public Logger getLogger() {
        return logger;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    protected abstract void sendError(HttpServletRequest req, HttpServletResponse resp, int errorCode, String errorMessage, Object errorValue);

    protected void sendError(HttpServletRequest req, HttpServletResponse resp, JsonRpcError error ){
        sendError( req, resp, error.getCode(), error.getMessage(), error.getData() );
    }

    protected void sendResponse( HttpServletRequest req, HttpServletResponse resp, Object response ){
        try {
            if( logger != null )
                logger.debug( "Sending response message:" + response );
            HttpServices.sendJSONMessage(req, resp, response);
        } catch (StreamLineException e) {
            e.printStackTrace();
        }
    }

    protected boolean checkCallPreconditions(HttpServletRequest req, HttpServletResponse resp){
        try {
            if( apiKeyRequired && (req.getParameter("api_key") == null || !apiKeyValidator.isValid( req.getParameter("api_key") ) ) )
                throw new StreamLineException( StreamLineException.ERROR_INVALID_CREDENTIALS, "API_KEY is required!");
            return true;
        } catch (StreamLineException e){
            sendError( req, resp, e.getErrorCode(), e.getMessage(), e.getPayLoad() );
        }

        return false;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendError( resp.SC_BAD_REQUEST, "Only post method is supported!" );
    }

    protected abstract void doInnerPost(HttpServletRequest req, HttpServletResponse resp) throws StreamLineException, IOException;

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        if( checkCallPreconditions(req, resp) ){
            try {
                doInnerPost( req, resp );
            } catch (StreamLineException e) {
                sendError( req, resp, e.getErrorCode(), e.getMessage(), e.getPayLoad() );
                e.printStackTrace();
            }  catch (Exception e) {
                sendError( req, resp, JsonRpcServices.ERROR_INTERNAL_ERROR );
                e.printStackTrace();
            }
        }
    }


}
